#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <signal.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <unistd.h>

#include "../../common/include/filesystem.h"
#include "../include/general.h"
#include "../include/commonPI.h"
#include "../include/common.h"

int
walkDirPI(char *path, char *relPath, char * snd_fifo, char * rcv_fifo)
{
    DIR *dip;
    struct dirent *dit;
    int i = 0;
    char *ext;
    char currPath[FILE_LEN];
    char currRelPath[FILE_LEN];

    /*
    * Open a directory stream to argv[1] and make sure
        * it's a readable and valid (directory) */
    if ((dip = opendir(path)) == NULL)
    {
        perror("opendir");
        return 0;
    }

    /*
    * Read in the files from argv[1] and print */
    while ((dit = readdir(dip)) != NULL)
    {
        i++;
        sprintf(currPath, "%s/%s", path, dit->d_name);

        /*TODO: Si se le pasa un path roto es probable que muera,
        no se pone ahora porque toda la info sale de readdir
        No envia archivos ni dirs que empiecen con '.'*/
        if (isDir(currPath))
        {
            if ( 0 == strcmp(".", dit->d_name) ||
                 0 == strcmp("..", dit->d_name) )
                continue;


            sprintf(currRelPath, "%s/%s", relPath, dit->d_name);


            if (snd_file_fifo (snd_fifo, rcv_fifo, currPath, 1) < 0)
            {
                fatal ("Error sending file\n");
            }

            walkDirPI(currPath, currRelPath, snd_fifo, rcv_fifo);
        }
        else
        {
            /* Don't send .mine's */
            ext = fileExt(dit->d_name);
            if (    0 == strcmp(".mine", ext) ||
                    0 == strcmp(".new", ext) )
                continue;

            if (snd_file_fifo (snd_fifo, rcv_fifo, currPath, 0) < 0)
            {
                fatal ("Error sending file\n");
            }
        }
    }

    /*
    * Close the stream. And check for errors. */
    if (closedir(dip) == -1)
    {
        perror("closedir");
        return 0;
    }

    return 1;
}



int sendFile (int priv, char * file_name, int file_size)
{
    FILE * file;
    int buf_size = BUF_LEN;
    char buf [BUF_LEN];
    int j, nwrite;

//    signal(SIGPIPE, SIG_IGN);

    if ((file = fopen (file_name, "rb")) == NULL){
        fprintf (stdout, "Error: Could not open file\n");
        return 0;
    }
    errno = 0;
    j=0;
    while (j < file_size)
    {
        buf_size = fread (buf, sizeof (char), BUF_LEN, file);
        j += buf_size;

        if ((nwrite = write(priv, buf, buf_size)) == -1)
            fatal("message write failed\n");
    }

    fprintf (stdout, "Sent file %s: %d bytes\n", file_name, j);
    fclose (file);
    return 1;
}

/* Write "n" bytes to a descriptor. */
/* Stevens */
ssize_t
writen(int fd, const void *vptr, size_t n)
{
   size_t      nleft;
   ssize_t     nwritten;
   const char  *ptr;

//   signal(SIGPIPE, SIG_IGN);
   errno=0;
   ptr = vptr;
   nleft = n;
   while (nleft > 0) {
       if ( (nwritten = write(fd, ptr, nleft)) <= 0) {
           if (nwritten < 0 && errno == EINTR)
               nwritten = 0;       /* and call write() again */
           else
               return(-1);         /* error */
       }
       if(errno == EPIPE)
           return -1;

       nleft -= nwritten;
       ptr   += nwritten;
   }
   return(n);
}
/* end writen */

/* Read "n" bytes from a descriptor. */
/* Stevens */
ssize_t
readn(int fd, void *vptr, size_t n)
{
   size_t  nleft;
   ssize_t nread;
   char    *ptr;

   ptr = vptr;
   nleft = n;
   while (nleft > 0)
   {
       if ( (nread = read(fd, ptr, nleft)) < 0)
       {
           if (errno == EINTR)
               nread = 0;      /* and call read() again */
           else
               return(-1);
       } else if (nread == 0)
           break;              /* EOF */

       nleft -= nread;
       ptr   += nread;
   }
   return(n - nleft);      /* return >= 0 */
}

void sndFifoAns(char * fifo, int code)
{
    int fd, nwrite, anslen;
    char answ[3] = {0};

    sprintf (answ, "%d", code);
    anslen = strlen ((char *)answ);

    if ((fd = open(fifo, O_WRONLY)) < 0)
        fatal ("fifo open failed\n");

    if ((nwrite = write(fd, answ, anslen)) == -1)
        fatal("message write failed\n");

    if (close (fd))
        fatal ("fifo close failed\n");

    return;
}

int rcvFifoAns(char * fifo)
{

    int fdin, ans;
    char msgbuf[3] = {0};

    if ((fdin = open(fifo, O_RDONLY)) < 0)
        fatal ("fifo open failed\n");

    if (read (fdin, msgbuf, 2) < 0)
        fatal ("msg read failed\n");

    if (close (fdin))
        fatal ("fifo close failed\n");

    sscanf(msgbuf, "%d", &ans);

    return ans;
}


int
send_data_fifo(int fd, char * s_msg, int size)
{
    int n_size;

    if (size < 0 || s_msg == NULL)
        return -1;

    n_size = writeWithTimeout(fd, s_msg, size, TIMEOUT);

    if (n_size < 0)
        return -2;

    return 1;
}


int readsize (int fd)
{
    char chsize[17];
    int i = 0;
    int size;

    if (readWithTimeout(fd, chsize, 1, TIMEOUT) < 0)
        fatal ("msg read failed\n");

    if (chsize[i] == ',')
		if (readWithTimeout(fd, chsize, 1, TIMEOUT) < 0)
			fatal ("msg read failed\n");

    while (chsize[i] != ',')
    {
        if (readWithTimeout (fd, &(chsize[++i]), 1, TIMEOUT) < 0)
            fatal ("msg read failed\n");
    }

    chsize[i] = 0;
    sscanf (chsize, "%d", &size);

    return size;
}

int readcom (int fd)
{
    char chsize[17];
    int i = 0;
    int size;

    while (i < 2)
    {
        if (readWithTimeout (fd, &(chsize[i++]), 1, TIMEOUT) < 0)
            fatal ("msg read failed\n");
    }

    chsize[i] = 0;
    sscanf (chsize, "%d", &size);

    return size;
}

int readcomChPI (char * pipe)
{
    char chsize[17];
    int i = 0;
    int size;
    int fd;

    if ((fd = open(pipe, O_RDONLY)) < 0)
        fatal ("fifo open failed\n");

    while (i < 2)
    {
        if (readWithTimeout (fd, &(chsize[i++]), 1, TIMEOUT) < 0)
            fatal ("msg read failed\n");
    }

    chsize[i] = 0;
    sscanf (chsize, "%d", &size);

    if (close (fd))
        fatal ("fifo close failed\n");

    return size;
}

int readcomID (char * pipe)
{
    char chsize[17] = {0};
    int i = 0;
    int size, fd;

    if ((fd = open(pipe, O_RDONLY)) < 0)
        fatal ("fifo open failed\n");

    if (readWithTimeout (fd, &(chsize[i]), 1, TIMEOUT) < 0)
        fatal ("msg read failed\n");

    if (chsize[i] == ',')
        i = -1;

    while ( i == -1 || (chsize[i] != 0 && chsize[i] != ','))
    {
        i++;
        if (readWithTimeout (fd, &(chsize[i]), 1, TIMEOUT) < 0)
            fatal ("msg read failed\n");
    }

    chsize[i] = 0;
    sscanf (chsize, "%d", &size);

    if (close (fd))
        fatal ("fifo close failed\n");

    return size;
}
int snd_file_fifo (char * snd_fifo, char * rcv_fifo, char * fname, int isdir)
{
    unsigned int fsize;
    FILE * pfile;
    char buf[BUF_SIZE] = {0};
    int acum, nread, back, wfd, name_len, tot_len;
    char answ[MSGSIZE] = {0};
    char msg[MSGSIZE] = {0};
    int cant = 0;

    if ((wfd = open (snd_fifo, O_WRONLY)) < 0)
        fatal ("fifo open failed\n");

    if (isDir(fname) == 1)
    {
        sprintf(msg, "%d,%s", -1, fname);
        name_len = strlen(msg);
        sprintf(answ, "%d,%d,%d,%s", SND_FILE, name_len, -1, fname);

        tot_len = strlen (answ);

        if ((send_data_fifo (wfd, answ, tot_len)) < 0)
        {
            fatal ("send dir failed\n");
            return -1;
        }

        if ((back = rcvFifoAns(rcv_fifo)) != COM_ACK)
        {
            fatal ("Error received\n");
            return -1;
        }

        if (close (wfd) < 0)
            fatal ("fifo close failed\n");

        return 1;
    }

    fsize = getFileSize(fname);


    if ((pfile = fopen(fname, "r")) == NULL)
        fatal ("file open failed\n");

    sprintf(msg, "%d,%s", fsize, fname);
    name_len = strlen(msg);
    sprintf(answ, "%d,%d,%s", SND_FILE, name_len, msg);

    tot_len = strlen (answ);

    if ((send_data_fifo (wfd, answ, tot_len)) < 0)
    {
        fatal ("send file failed\n");
        return -1;
    }

    if ((back = rcvFifoAns(rcv_fifo)) != COM_ACK)
    {
        fatal ("Error received\n");
    }

    acum = 0;
    while (acum < fsize)
    {
        if ((fsize - acum) > BUF_SIZE)
            cant = BUF_SIZE;
        else
            cant = (fsize - acum);

        nread = fread(buf, 1, cant, pfile);
        if (nread < BUF_SIZE && nread < (fsize - acum))
        {
                fatal ("File read failure\n");
        }

        acum += nread;

        do
        {
            if ((send_data_fifo (wfd, buf, nread)) < 0)
            {
                fatal ("send dir failed\n");
                return -1;
            }

            back = rcvFifoAns(rcv_fifo);

        }while (back != COM_ACK);
    }

    if (close (wfd) < 0)
        fatal ("fifo close failed\n");

    if (fclose (pfile) == EOF)
        fatal ("file close failed\n");

    return 1;

}
